home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC] / NeXTSTEP 3.3 Dev Intel.iso / NextDeveloper / Headers / dbkit / DBBinder.h < prev    next >
Text File  |  1994-05-16  |  17KB  |  425 lines

  1. /*
  2. **      DBBinder.h
  3. **      Database Kit, Release 3.0
  4. **      Copyright (c) 1992, NeXT Computer, Inc.  All rights reserved. 
  5. */
  6.  
  7. #import <objc/Object.h>
  8. #import <dbkit/protocols.h>
  9. #import <dbkit/enums.h>
  10.  
  11. #import <ansi/stdarg.h>
  12. #import <objc/List.h>
  13. #import <streams/streams.h>
  14. #import <mach/cthreads.h>
  15.  
  16. @class DBQualifier;
  17. @class DBDatabase;
  18. @class DBValue;
  19.  
  20. /*
  21. ** The DBBinder is a class which "connects" objects and variables within
  22. **  a NextStep program to external data.  The connect can be bidirectional,
  23. **  that is, values from NextStep can be either propagated to or slaved to
  24. **  the external database.  There are three verbs for modifying external
  25. **  data: "insert", "update", and "delete".  Data can be pulled from the
  26. **  database with "select" and "fetch".  Lastly, "evaluate" can be used to
  27. **  perform either function.
  28. **
  29. ** DBBinder is composed of two internal pieces --
  30. ** (1) mappings, which link DBDataPaths (which can be thought of as
  31. **     pointers to external data) to any number of DBDataWraps (which
  32. **     correspond to internal NextStep "pointers")  When selecting data from
  33. **     the external database into the app, the dataPaths help locate
  34. **     the data, and then the dataWraps are used to load the data into the
  35. **     app's objects.  When loading data from the app to the database, the
  36. **     dataWraps provide data, which is put into the locations referred to
  37. **     by the dataPaths.
  38. ** (2) qualifiers, which are a list of query expressions which are combined
  39. **     by the adaptor into a single expression.  The qualifiers can be built
  40. **     from any combination of objects that recognize the stringValue message,
  41. **     although its common to use DBString, DBExpressionList, DBDataWrap,
  42. **     and DBDataGuide objects as the basic building blocks.  The qualifiers
  43. **     reside in the DBDataSet for the binder.
  44. */
  45.  
  46. @interface DBBinder : Object <DBCursorPositioning>
  47. {
  48. @public
  49.   id database;               // remote source of info
  50.   id recordPrototype;        // the template object (an instance)
  51.   id container;              // the repository for recordPrototype copies
  52.   id delegate;               // receive channel notification, if used
  53.  
  54. @private
  55.   id _qualifier;             // the DBQualifier (optional)
  56.   id _properties;         // a list of DBDataPaths or DBAttributes
  57.   id _mappingsByProperty;    // HashTable of BinderMappings by dataPath
  58.   id _private;
  59.  
  60.   mutex_t _protoLock;        // lock for recordPrototype access
  61.   condition_t _dataAvailable;// condition for async fetch
  62.   cthread_t _fetchThread;    // thread that implements async fetch
  63.   void *_fetchMsg;           // mach message used in async fetch
  64.  
  65.   unsigned _currentPosition; // for cursoring
  66.   unsigned _fetchLimit;         // to control number of rows fetched
  67.   struct {
  68. #if    __BIG_ENDIAN__
  69.     BOOL abortFlag:1;         // used by cancel
  70.     BOOL flushEnabled:1;     // whether container is emptied on success
  71.     BOOL freeOnFlush:1;      // whether to free objects in container on flush
  72.     BOOL limitHit:1;         // fetchLimit has been exceeded
  73.     BOOL fetchDone:1;        // when caching, only one fetch done per eval
  74.     BOOL ignoreDuplicates:1; // select distinct, versus select all
  75.     BOOL sharesContext:1;    // shared transaction context for selects
  76.     BOOL ownsRecordPrototype:1; // recordPrototype was created by binder
  77.     int _RESERVED:8;
  78. #else    __BIG_ENDIAN
  79.     int _RESERVED:8;
  80.     BOOL ownsRecordPrototype:1; // recordPrototype was created by binder
  81.     BOOL sharesContext:1;    // shared transaction context for selects
  82.     BOOL ignoreDuplicates:1; // select distinct, versus select all
  83.     BOOL fetchDone:1;        // when caching, only one fetch done per eval
  84.     BOOL limitHit:1;         // fetchLimit has been exceeded
  85.     BOOL freeOnFlush:1;      // whether to free objects in container on flush
  86.     BOOL flushEnabled:1;     // whether container is emptied on success
  87.     BOOL abortFlag:1;         // used by cancel
  88. #endif    __BIG_ENDIAN__
  89.   } _flags;
  90.   NXZone *_tempZone;         // recursive structures built here for easy free
  91.   NXZone *_protoZone;         // zone for dynamically created objects
  92. }
  93.  
  94. - init;
  95. - initForDatabase:aDb
  96.     withProperties:(List*)aList andQualifier:(DBQualifier*)aQualifier;
  97. - free;
  98.  
  99. /*
  100. ** Setting the database allows you to use the database for transaction
  101. **  control and as a source of data.  It also has a default data dictionary,
  102. **  used by dynamically created protoClasses.
  103. */
  104. - setDatabase:(DBDatabase*)aDatabase;
  105. - (DBDatabase*)database;
  106.  
  107. /*
  108. ** getProperties fills a List based on the binder's mappings;
  109. **  the order is the same order as the "target list" in the query.
  110. **  Both the list and the properties that are contained in it
  111. **  should not be freed, since they are not copied.
  112. **
  113. ** IMPORTANT!  setProperties: causes a reset of the entire binder!
  114. **
  115. ** setProperties: returns the new recordPrototype object
  116. **
  117. ** addProperty is used to describe a class to be built on the fly.
  118. **  The class is then used to create a recordPrototype instance that is used as
  119. **  the binder's recordPrototype.  This is an alternative to using
  120. **  setRecordPrototype. createRecordPrototype will automatically be called
  121. **  by data-producing methods.
  122. **
  123. ** Calling addProperty or removeProperty should eventually be followed
  124. **  by a call to createRecordPrototype.  Because of this, you normally want to
  125. **  call reset before the first addProperty.
  126. **
  127. ** addProperty: either creates a new mapping or returns the pre-existing
  128. **  mapping for the argument. createRecordPrototype returns new
  129. **  recordPrototype or nil.
  130. */
  131. - (List*)getProperties:(List*)aList;
  132. - (List*)setProperties:(List*)aList;
  133.  
  134. - addProperty:anObject;
  135. - removePropertyAt:(unsigned)index;
  136. - createRecordPrototype;
  137.  
  138. /*
  139. ** These are the main feature...and are pretty self explanatory.  Evaluate
  140. **  is in fact the routine that does all the work usually -- the other verbs
  141. **  take the binder and format it into the expected query language.
  142. **
  143. ** DBBinder is basically a cursor into NextStep -- fetch will load the objects
  144. **  that are contained in the DBDataWraps in the mappings with the next "row"
  145. **  of data.
  146. **
  147. ** Note that a select will normally do a fetch, but that an evaluate leaves
  148. **  it to the programmer to do a fetch if necessary.  Also note that it is
  149. **  good practice to call cancelFetch: when finished, since many databases
  150. **  dedicate expensive runtime structures to a binder, which can be reclaimed
  151. **  upon cancelFetch.
  152. */
  153. - insert;
  154. - select;
  155. - update;
  156. - delete;
  157.  
  158. - (BOOL)evaluateString:(const unsigned char*)aString, ...;
  159.  
  160. - fetch;
  161. - cancelFetch;
  162.  
  163. /*
  164. ** Having used the DBCursorPositioning methods for positioning, here is the
  165. **  method to set/retrieve values from the recordPrototype.  The DBValue
  166. **  pointer that is returned is owned by the binder, and will change
  167. **  frequently. It is not safe to keep references to one of these DBValues
  168. **  lying around; you should retrieve it immediately before use.
  169. **
  170. ** Note that not all properties in the binder will have a value,
  171. **  since some properties can be embedded in the qualifier tree,
  172. **  rather than having a binding associated with them.  In this case, the call
  173. **  will return nil.
  174. **
  175. ** Also note that there is a maxiumum of one value for a given
  176. **  property.  This means that complex qualifiers MUST use the qualifier
  177. **  mechanism -- the mechanism by which a binding can be used as a qualifier
  178. **  is really meant for qualified updates based on a previous select.
  179. */
  180. - (DBValue*)valueForProperty:(id<DBProperties>)aProperty;
  181.  
  182. /*
  183. ** Adaptors that support sorted results can utilize these methods.  Multiple
  184. **  property sorts are handled in the order in which they are added.
  185. */
  186. - addRetrieveOrder:(DBRetrieveOrder)anOrder for:(id<DBProperties>)aProperty;
  187. - removeRetrieveOrderFor:(id<DBProperties>)aProperty;
  188. - (DBRetrieveOrder)retrieveOrderFor:(id<DBProperties>)aProperty;
  189. - (unsigned)positionInOrderingsFor:(id<DBProperties>)aProperty;
  190.  
  191. /*
  192. ** The binder delegate can act as a control, denying or approving
  193. **  operations.  Any of the methods that return BOOL will either confirm or
  194. **  deny the operation in the head binder.  For example, if binderWillInsert
  195. **  returns NO from the delegate, the insert will not be executed.
  196. */
  197. - delegate;
  198. - setDelegate:anObject;
  199.  
  200. - read:(NXTypedStream*)s;
  201. - write:(NXTypedStream*)s;
  202.  
  203. @end
  204.  
  205. @interface Object (BinderDelegate)
  206.  
  207. - (BOOL)binderWillInsert:aBinder;
  208. - binderDidInsert:aBinder;
  209. - (BOOL)binderWillSelect:aBinder;
  210. - binderDidSelect:aBinder;
  211. - (BOOL)binderWillUpdate:aBinder;
  212. - binderDidUpdate:aBinder;
  213. - (BOOL)binderWillDelete:aBinder;
  214. - binderDidDelete:aBinder;
  215.  
  216. - (BOOL)binder:aBinder willEvaluateString:(const unsigned char*)aString;
  217. - binder:aBinder didEvaluateString:(const unsigned char*)aString;
  218.  
  219. - (BOOL)binderWillFetch:aBinder;
  220. - binderDidFetch:aBinder;
  221.  
  222. @end
  223.  
  224. @interface DBBinder (Advanced)
  225. /*
  226. ** Data is moved from an adaptor into a binder through the use of objective-C
  227. **  objects.  The binder's recordPrototype object acts as a shuttle between the
  228. **  external database and the binder.
  229. **
  230. ** If there is no recordPrototype object, but the proto class has been set, an
  231. **  object of that class will be created; a subclass will be created if there
  232. **  are more results than the recordPrototype can handle.
  233. ** 
  234. ** If no recordPrototype or protoclass has been specified, a class which will
  235. **  suffice is created dynamically.  This class defaults to being a subclass
  236. **  of Object, although the superclass, can be specified using
  237. **  setDynamicRecordSuperclassName.
  238. **
  239. ** To turn off the effect of these methods, set their arguments to NULL.
  240. **  These should not be used without a good understanding of the dynamic
  241. **  class creation protocol.
  242. **
  243. ** Note that these apply globally to all binders using dynamic class creation!
  244. */
  245. + setDynamicRecordSuperclassName:(const char*)aName;
  246. + setDynamicRecordClassName:(const char*)aName;
  247.  
  248. /*
  249. ** Reset clears the binder, and frees any internal structures that it had
  250. **  created.  If you passed your own ids, its up to you to free them, including
  251. **  the recordPrototype, the properties, and the set.  (Internal
  252. **  DBDataPaths and DBDataWraps are freed, but this does not free the
  253. **  objects that are wrapped inside!)
  254. **
  255. ** scratchZone returns an NXZone pointer that can be used to allocate objects
  256. **  that will then be freed en masse whenever a reset is done to the binder.
  257. **  This can be a very efficient way of allocating numerous support objects
  258. **  that exist for a single query.  Note that the zone returned will vary
  259. **  from reset to reset!
  260. **
  261. ** Flush is called by objects that return data (typically adaptors).  Use
  262. **  setFlushEnabled and setFreeObjectsOnFlush to affect its behavior.
  263. */
  264. - reset;
  265. - (BOOL)flush;
  266. - (NXZone*)scratchZone;
  267.  
  268. /*
  269. ** setQualifier returns the old qualifier -- it does not cause a reset.
  270. */
  271. - setQualifier:(DBQualifier*)aQualifier;
  272. - (DBQualifier*)qualifier;
  273.  
  274. /*
  275. ** setRecordPrototype is useful for filling existing classes with data from a
  276. **  database.  Pass a prototypical object in, do the query, and then use
  277. **  the container full o' newly minted objects.  setRecordPrototype: is usually
  278. **  followed by one or more calls to associateXXX:
  279. **
  280. ** The recordPrototype is used as a conduit for data from the adaptor to
  281. **  the binder. When the newly stuffed recordPrototype arrives, the
  282. **  appropriate parts are distributed to any "external mappings" that exist.
  283. **  Because of this, external mappings must always have a valid reference 
  284. **  to the recordPrototype. (This is managed automatically by the binder.)
  285. */
  286. - setRecordPrototype:anObject;
  287. - recordPrototype;
  288. - (BOOL)ownsRecordPrototype;
  289.  
  290. /*
  291. ** These establish which selectors or instance variables in a custom
  292. **  recordPrototype will be mapped onto the database.
  293. */
  294. - associateRecordIvar:(const char*)ivar 
  295.     withProperty:(id<DBProperties>)aProperty;
  296. - associateRecordSelectors:(SEL)set :(SEL)get
  297.     withProperty:(id<DBProperties>)aProperty;
  298.  
  299. /*
  300. ** The container holds the objects to be submitted to the database, or the
  301. **  objects that result from a query.  setContainer: returns the old container
  302. **  so that it can be freed.
  303. **
  304. ** In order to use asynchronous fetching of data safely, the container should
  305. **  either be threadsafe, or all access to the container should be through
  306. **  the binder.  (setTo, etc.)
  307. **
  308. ** Providing a container turns on "caching" of data -- setting it to nil
  309. **  turns it off.
  310. **
  311. ** Containers provide random access positioning -- these routines return
  312. **  an id, which corresponds to the "current object".  
  313. **
  314. ** All of these methods, except setNext:, will raise an exception if there
  315. **  is no container.
  316. **
  317. ** If there is an asyncFetch going on, these calls will BLOCK until the
  318. **  requested "row" is available.  These are actually the preferred interface
  319. **  for getting at the results of an asynchronous fetch while the fetch
  320. **  is in progress.
  321. **
  322. ** All of the positioning methods except for setNext: (which works in all
  323. **  cases) will raise an exception if there is no container.
  324. */
  325. - setContainer:(id<DBContainers>)anObject;
  326. - (id<DBContainers>)container;
  327.  
  328. /*
  329. ** This regulates whether the container is emptied on every data generating
  330. **  message, and if it is emptied, then whether the objects in the container
  331. **  are freed.
  332. */
  333. - setFlushEnabled:(BOOL)yn;
  334. - (BOOL)isFlushEnabled;
  335. - setFreeObjectsOnFlush:(BOOL)yn;
  336. - (BOOL)areObjectsFreedOnFlush;
  337.  
  338. /*
  339. ** fetchAsync forks a thread and returns.
  340. **
  341. ** It then continues to stuff results into the container until fetchData:
  342. **  returns nil.  Because of this, its possible to have an adaptor that could
  343. **  be streaming results back, while accepting changes through evaluate: or
  344. **  other calls...  This will be particularly handy with news feeds, etc.
  345. **
  346. ** It is, however, fatal to change the structure of the binder will the
  347. **  fetch thread is running -- this means that only the non-structural calls
  348. **  are safe, such as cancelFetch:
  349. **
  350. **  fetchInThread will fetch data into the container in a separate thread.
  351. **   There must be a container for this routine to work.  If this is used
  352. **   from a non-NeXTstep program, then checkThreadedFetchCompletion: can be
  353. **   used to sync with the thread.
  354. */
  355. - selectWithoutFetching;
  356. - fetchInThread;
  357.  
  358. - checkThreadedFetchCompletion:(double)timeout;
  359.  
  360. /*
  361. ** This can be called by an adaptor -- the default behavior is to defer to
  362. **  the database, who in turn defers to its delegate.  This method is
  363. **  called immediately before an expression is evaluated.  If NO is returned,
  364. **  the expression is cancelled.  Normally, this method is called for every
  365. **  query expression evaluated.
  366. */
  367. - (BOOL)adaptorWillEvaluateString:(const unsigned char*)aString;
  368.  
  369. /*
  370. ** If you'd only like only unique rows for the qualifier (and the adaptor
  371. **  supports this) then setIgnoreDuplicateResults:YES
  372. */
  373. - setIgnoresDuplicateResults:(BOOL)yn;
  374. - (BOOL)ignoresDuplicateResults;
  375.  
  376. /*
  377. ** Databases that support "protected" access through the notion of
  378. **  transaction processing have a "cursor" that can be shared among a number
  379. **  of binders.  The default is for this "cursor" to be used by any data
  380. **  modifying operations (like insert, delete, or update).  Select, evaluate,
  381. **  and fetch, however, get their own "cursors" by default.  To change this
  382. **  behavior for a binder, set this flag to YES.  The binder will then use
  383. **  the shared "cursor" even for these operations.
  384. **
  385. ** NOTE HOWEVER: when using a single shared resource, all operations must fully
  386. **  complete before the next is invoked.  (selects and updates could not be
  387. **  interleaved, for instance...)  The default behavior permits a looser
  388. **  ordering than this, but is also potentially more expensive in terms of
  389. **  resources.
  390. **
  391. ** ALSO NOTE: [setSharesContext:YES] will turn OFF flushing for a binder!
  392. **
  393. ** By setting the context to be shared, "select for update" can be implemented,
  394. **  in which the selected items are locked until updated.
  395. **
  396. **    [someBinder setSharesContext:YES];
  397. **     [someDatabase beginTransaction];
  398. **    [someBinder select:self];
  399. **      ...processing here...
  400. **    [someBinder update:self];
  401. **     [someDatabase endTransaction];
  402. */
  403. - setSharesContext:(BOOL)yn;
  404. - (BOOL)sharesContext;
  405.  
  406. /*
  407. ** A sanity check for retrieving data -- note that multiple fetches can
  408. **  be performed to "continue" an operation that was stopped because of the
  409. **  limit.  This limit only applies when a container is in place and a
  410. **  synchronous fetch is used.
  411. */
  412. - (unsigned)maximumRecordsPerFetch;
  413. - setMaximumRecordsPerFetch:(unsigned)aRecordCount;
  414. - (BOOL)recordLimitReached;
  415.  
  416. @end
  417.  
  418. @interface List (DBContainers)
  419.  
  420. - addObject:anObject forBinder:(DBBinder*)aBinder;
  421. - (unsigned int)prepareForBinder:(DBBinder*)aBinder;
  422. - objectAt:(unsigned int)index forBinder:(DBBinder*)aBinder;
  423.  
  424. @end
  425.